package com.lucy.starter.aspect;

import cn.hutool.core.lang.Validator;
import cn.hutool.extra.servlet.ServletUtil;
import com.lucy.starter.anno.WebLog;
import com.lucy.starter.constant.MethodTypeConst;
import com.lucy.starter.domain.WebLogDTO;
import com.lucy.starter.domain.vo.BaseUser;
import com.lucy.starter.event.WebLogCreatedEvent;
import com.lucy.starter.loader.AuthService;
import com.lucy.starter.redis.RedisCache;
import com.lucy.starter.utils.JsonUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @author kindear
 * 接口请求日志
 */
@Slf4j
@Aspect
@Component
public class WebLogAspect {
    /**
     * 引入鉴权系统服务
     */
    @Autowired
    AuthService authService;

    /**
     * 服务上下文
     */
    @Resource
    ApplicationContext context;

    /**
     * 以自定义注解作为切入
     */
    @Pointcut("@annotation(com.lucy.starter.anno.WebLog)")
    public void WebLog(){}

    /**
     * 切面
     * @param joinPoint 切面信息
     */
    @Around("WebLog()")
    public Object handleWebLog(ProceedingJoinPoint joinPoint) throws Throwable {
        return recordWebLog(joinPoint);
    }

    //
    public Object recordWebLog(ProceedingJoinPoint joinPoint) throws Throwable {
        // 初始化日志类
        WebLogDTO apiLog = new WebLogDTO();
        // 获取注解
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        // 获取自定义注解信息
        WebLog webLog = method.getAnnotation(WebLog.class);
        Boolean isLogin = authService.isLogin();
        // 根据登录态处理
        if (isLogin){
            BaseUser baseUser = authService.getUser();
            apiLog.setOperatorId(baseUser.getUserId());
            apiLog.setOperator(baseUser.getUserName());
        }else {
            apiLog.setOperator("匿名用户");
        }


        // 获取模块信息
        String module = webLog.module();
        if (Validator.isEmpty(module)){
            module = webLog.value();
        }
        apiLog.setModule(module);

        // 获取类与方法信息
        Object target = joinPoint.getTarget();
        String className = target.getClass().getName();
        String methodName = target.getClass().getMethod(methodSignature.getName(), methodSignature.getParameterTypes()).getName();
        apiLog.setClazz(className);
        apiLog.setMethod(methodName);
        // 获取请求IP
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        assert attributes != null;
        HttpServletRequest request = attributes.getRequest();
        String reqMethod = request.getMethod();
        // 获取操作信息
        String operate = webLog.operate();
        if (Validator.isEmpty(operate)){
            switch (reqMethod){
                case "GET":{
                    apiLog.setContent(MethodTypeConst.READ);
                    break;
                }
                case "POST":{
                    apiLog.setContent(MethodTypeConst.CREATE);
                    break;
                }
                case "PUT":{
                    apiLog.setContent(MethodTypeConst.UPDATE);
                    break;
                }
                case "DELETE":{
                    apiLog.setContent(MethodTypeConst.DELETE);
                    break;
                }
            }
        }else {
            apiLog.setContent(operate);
        }

        String clientIp = ServletUtil.getClientIP(request);
        apiLog.setClientIp(clientIp);
        // 设置请求参数
        //获取入参
        Object[] objs = joinPoint.getArgs();
        String[] argNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames(); // 参数名
        Map<String, Object> paramMap = new HashMap<String, Object>();
        for (int i = 0; i < objs.length; i++) {
            paramMap.put(argNames[i], objs[i]);
        }
        apiLog.setParams(JsonUtil.obj2String(paramMap));
        // 设置开始时间
        long startTime = System.currentTimeMillis();
        apiLog.setStartTime(new Date());

        try {
            Object object = joinPoint.proceed();
            apiLog.setStatus(1);
            apiLog.setEndTime(new Date());
            apiLog.setTotalTime((int) (System.currentTimeMillis() - startTime));
            // 返回值
            if (null != object) {
                String returnData = JsonUtil.obj2String(object);
                apiLog.setReturnData(returnData);
            }
            return object;
        } catch (Throwable e) {
            apiLog.setStatus(0);
            apiLog.setEndTime(new Date());
            apiLog.setTotalTime((int) (System.currentTimeMillis() - startTime));
            apiLog.setContent(e.getMessage());
            throw e;
        } finally {
            // 发布日志事件
            context.publishEvent(new WebLogCreatedEvent(this,apiLog));
        }
    }

}
